﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Device.Location;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Microsoft.Devices;
using Microsoft.Phone.Controls;
using Tequila.AugmentedReality.CameraStates;
using Tequila.AugmentedReality.Data;
using Tequila.AugmentedReality.Views;
using Matrix = Microsoft.Xna.Framework.Matrix;

namespace Tequila.AugmentedReality
{
    public partial class AugmentedRealityControl
    {
        private PhotoCamera _camera;
        private CameraBaseState _cameraState;
        private PageOrientation _currentOrientation = PageOrientation.LandscapeLeft;
        private Dictionary<PointOfInterest, CameraPoi> _pois;

        public static readonly DependencyProperty POIControlTemplateProperty =
            DependencyProperty.Register("POIControlTemplate",
            typeof(DataTemplate), 
            typeof(AugmentedRealityControl),
            new PropertyMetadata(default(DataTemplate)));

        public static readonly DependencyProperty PointsOfInterestProperty =
            DependencyProperty.Register("PointsOfInterest", 
            typeof (IEnumerable<PointOfInterest>), 
            typeof (AugmentedRealityControl),
            new PropertyMetadata(default(IEnumerable<PointOfInterest>), PointsOfInterestPropertyChanged));

        public static readonly DependencyProperty DeviceRotationProperty =
            DependencyProperty.Register("DeviceRotation",
            typeof(Matrix),
            typeof(AugmentedRealityControl),
            new PropertyMetadata(DeviceRotationDependencyPropertyChanged));

        public static readonly DependencyProperty RadiusProperty =
            DependencyProperty.Register("Radius",
            typeof(int),
            typeof(AugmentedRealityControl),
            new PropertyMetadata(3000));

        public static readonly DependencyProperty CurrentLocationProperty =
            DependencyProperty.Register("CurrentLocation",
            typeof(GeoCoordinate),
            typeof(AugmentedRealityControl),
            new PropertyMetadata(GeoCoordinate.Unknown, PropertyChangedCallback));

        private static void PropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
        {
            var ctrl = (AugmentedRealityControl) dependencyObject;
            ctrl.UpdateScale();
        }

        private void UpdateScale()
        {
            if (CurrentLocation == null || CurrentLocation.IsUnknown || _cameraState == null || _pois == null)
            {
                return;
            }

            foreach (var poiControl in _pois)
            {
                poiControl.Value.Scale = CalculatePlaceIconScaling(CurrentLocation.GetDistanceTo(poiControl.Key.Location));
            }
        }

        public event EventHandler<SelectedItemEventArgs> ItemTap = delegate { };

        public AugmentedRealityControl()
        {
            InitializeComponent();
            UpdateCameraState();
            SizeChanged += ControlSizeChanged;
        }
       
        public DataTemplate POIControlTemplate
        {
            get
            {
                return (DataTemplate)GetValue(POIControlTemplateProperty);
            }
            set
            {
                SetValue(POIControlTemplateProperty, value);
            }
        }


        public IEnumerable<PointOfInterest> PointsOfInterest
        {
            get
            {
                return (IEnumerable<PointOfInterest>)GetValue(PointsOfInterestProperty);
            }
            set
            {
                SetValue(PointsOfInterestProperty, value);
            }
        }

        public Matrix DeviceRotation
        {
            get
            {
                return (Matrix)GetValue(DeviceRotationProperty);
            }

            set
            {
                SetValue(DeviceRotationProperty, value);
            }
        }

        public int Radius
        {
            get
            {
                return (int)GetValue(RadiusProperty);
            }

            set
            {
                SetValue(RadiusProperty, value);
            }
        }

        public GeoCoordinate CurrentLocation
        {
            get
            {
                return (GeoCoordinate)GetValue(CurrentLocationProperty);
            }

            set
            {
                SetValue(CurrentLocationProperty, value);
            }
        }

        public void TurnOnCamera()
        {
            if (_camera != null)
            {
                return;
            }

            _camera = new PhotoCamera();
            
            if (_camera != null)
            {
                CameraView.SetSource(_camera);
            }
        }

        public void TurnOffCamera()
        {
            if (_camera != null)
            {
                _camera.Dispose();
                _camera = null;
            }
        }

        public void OrientationChanged(PageOrientation pageOrientation)
        {
            _currentOrientation = pageOrientation;
            UpdateCameraState();
        }

        private void OnRotationChanged()
        {
            if (_cameraState == null || _cameraState.ViewportIsZero)
                return;
               
            UpdatePOIView(DeviceRotation);
        }

        private void UpdatePOIView(Matrix deviceRotationMatrix)
        {
            if (CurrentLocation == null || CurrentLocation.IsUnknown || _cameraState == null)
            {
                return;
            }

            foreach (var poiControl in _pois)
            {
                
                var locationVector = ProjectionArithmetics.CalculateLocationVector3(CurrentLocation, poiControl.Key.Location);                

                _cameraState.UpdatePlacePosition(poiControl.Value, locationVector, deviceRotationMatrix);
                
                
            }
        }
       
        private void CreateInternalCollection()
        {
            _pois = PointsOfInterest.ToDictionary(poi => poi, poi => new CameraPoi(poi.Data));

            UpdateScale();

            PoiContainer.ItemsSource = _pois.Select(kv => kv.Value);
        }

        private float CalculatePlaceIconScaling(double distance)
        {
            const float minScaling = 0.4f;
            const float maxScaling = 1.6f;

            var scaleMultiplier = maxScaling / Radius;
            var scaling = maxScaling - (float)distance * scaleMultiplier;
            if (scaling < minScaling)
            {
                scaling = minScaling;
            }

            return scaling;
        }

        private void UpdateCameraState()
        {
            switch (_currentOrientation)
            {
                case PageOrientation.LandscapeLeft:
                    VideoContainer.RenderTransform = null;
                    _cameraState = new LandscapeLeftCameraBaseState((int) LayoutRoot.ActualWidth,
                                                                    (int) LayoutRoot.ActualHeight, Radius);
                    _cameraState.Initialize();
                    break;
                case PageOrientation.LandscapeRight:
                    _cameraState = new LandscapeRightCameraBaseState((int) LayoutRoot.ActualWidth,
                                                                     (int) LayoutRoot.ActualHeight, Radius);
                    _cameraState.Initialize();
                    VideoContainer.RenderTransform = new RotateTransform
                                                         {
                                                             Angle = 180,
                                                             CenterX = ActualWidth/2,
                                                             CenterY = ActualHeight/2
                                                         };
                    break;
            }
        }

        private void CameraPoiViewTap(object sender, EventArgs e)
        {
            var poiView = (CameraPoiView) sender;
            ItemTap(this, new SelectedItemEventArgs(poiView.Data));
        }

        private void ControlSizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateCameraState();
        }

        private static void DeviceRotationDependencyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = (AugmentedRealityControl)d;

            sender.OnRotationChanged();
        }

        private static void PointsOfInterestPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
        {
            var control = (AugmentedRealityControl)d;

            control.CreateInternalCollection();

            var oldObservableCollection = args.OldValue as INotifyCollectionChanged;
            if (oldObservableCollection != null)
            {
                oldObservableCollection.CollectionChanged -= control.ObservableCollectionOnCollectionChanged;
            }

            var observableCollection = args.NewValue as INotifyCollectionChanged;
            if (observableCollection != null)
            {
                observableCollection.CollectionChanged +=  control.ObservableCollectionOnCollectionChanged;
            }
        }

        private void ObservableCollectionOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
        {
            CreateInternalCollection();
        }

    }
}
